package org.programmerplanet.ant.taskdefs.jmeter;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Execute;
import org.apache.tools.ant.taskdefs.LogStreamHandler;
import org.apache.tools.ant.types.CommandlineJava;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

public class RunTestPlan extends Task implements Runnable{

    private File testPlanFile;
    private File resultLogFile;
    private File jmeterJar;
    private File jmeterProperties;
    private File jmeterLogFile;
    private File additionalUserProperties;
    private File jmeterHome;
    private ArrayList<?> jvmArgs;
    private ArrayList<?> jmeterArgs;
    private ArrayList<?> jmProperties;
    private boolean runRemote;
    private String proxyHost;
    private String proxyPort;
    private String proxyUser;
    private String proxyPass;
    private String logLevel;
    private ArrayList<File> resultLogFiles;

    public RunTestPlan(File testPlanFile,
                       File resultLogFile,
                       File jmeterJar,
                       File jmeterProperties,
                       File jmeterLogFile,
                       File additionalUserProperties,
                       File jmeterHome,
                       ArrayList<?> jvmArgs,
                       ArrayList<?> jmeterArgs,
                       ArrayList<?> jmProperties,
                       boolean runRemote,
                       String proxyHost,
                       String proxyPort,
                       String proxyUser,
                       String proxyPass,
                       String logLevel,
                       ArrayList<File> resultLogFiles){
        this.testPlanFile = testPlanFile;
        this.resultLogFile = resultLogFile;
        this.jmeterJar = jmeterJar;
        this.jmeterProperties = jmeterProperties;
        this.jmeterLogFile = jmeterLogFile;
        this.additionalUserProperties = additionalUserProperties;
        this.jmeterHome = jmeterHome;
        this.jvmArgs = jvmArgs;
        this.jmeterArgs = jmeterArgs;
        this.jmProperties = jmProperties;
        this.runRemote = runRemote;
        this.proxyHost = proxyHost;
        this.proxyPort = proxyPort;
        this.proxyUser = proxyUser;
        this.proxyPass = proxyPass;
        this.logLevel = logLevel;
        this.resultLogFiles = resultLogFiles;
    }

    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see Thread#run()
     */
    @Override
    public void run() {
        log("Executing test plan: " + testPlanFile + " ==> " + resultLogFile, Project.MSG_INFO);
        resultLogFiles.add(resultLogFile);

        CommandlineJava cmd = new CommandlineJava();

        cmd.setJar(jmeterJar.getAbsolutePath());

        // Set the JVM args
        for (Object arg : jvmArgs) {
            Arg jvmArg = (Arg) arg;
            cmd.createVmArgument().setValue(jvmArg.getValue());
        }

        // Set the JMeter args
        for (Object arg : jmeterArgs) {
            Arg jmeterArg = (Arg) arg;
            cmd.createArgument().setValue(jmeterArg.getValue());
        }

        // non-gui mode
        cmd.createArgument().setValue("-n");
        // the properties file
        if (jmeterProperties != null) {
            cmd.createArgument().setValue("-p");
            cmd.createArgument().setValue(jmeterProperties.getAbsolutePath());
        }
        // the jmeter log file
        if (jmeterLogFile != null) {
            cmd.createArgument().setValue("-j");
            cmd.createArgument().setValue(jmeterLogFile.getAbsolutePath());
        }

        // add user property
        if (additionalUserProperties != null) {
            cmd.createArgument().setValue("-q");
            cmd.createArgument().setValue(additionalUserProperties.getAbsolutePath());
        }
        // the test plan file
        cmd.createArgument().setValue("-t");
        cmd.createArgument().setValue(testPlanFile.getAbsolutePath());
        // the result log file
        cmd.createArgument().setValue("-l");
        cmd.createArgument().setValue(resultLogFile.getAbsolutePath());
        // run remote servers?
        if (runRemote) {
            cmd.createArgument().setValue("-r");
        }

        // the proxy host
        if ((proxyHost != null) && (proxyHost.length() > 0)) {
            cmd.createArgument().setValue("-H");
            cmd.createArgument().setValue(proxyHost);
        }
        // the proxy port
        if ((proxyPort != null) && (proxyPort.length() > 0)) {
            cmd.createArgument().setValue("-P");
            cmd.createArgument().setValue(proxyPort);
        }
        // the proxy user
        if ((proxyUser != null) && (proxyUser.length() > 0)) {
            cmd.createArgument().setValue("-u");
            cmd.createArgument().setValue(proxyUser);
        }
        // the proxy password
        if ((proxyPass != null) && (proxyPass.length() > 0)) {
            cmd.createArgument().setValue("-a");
            cmd.createArgument().setValue(proxyPass);
        }
        // JMeter log level
        // eg. jmeter -n -t xxx.jmx -L DEBUG
        // eg. jmeter -n -t xxx.jmx -L jmeter.engine=DEBUG -L org.apache.http=DEBUG
        // eg. <jmeter jmeterhome="${jmeter.home}" ... loglevel="DEBUG">
        // eg. <jmeter jmeterhome="${jmeter.home}" ... loglevel="jmeter.engine=DEBUG,org.apache.http=DEBUG">
        if ((logLevel != null) && (logLevel.length() > 0)) {
            String[] categories = logLevel.split(",");
            for (String category: categories) {
                cmd.createArgument().setValue("-L");
                cmd.createArgument().setValue(category);
            }
        }

        // the JMeter runtime properties
        for (Object property : jmProperties) {
            Property jmProperty = (Property) property;
            if (jmProperty.isValid()) {
                cmd.createArgument().setValue((jmProperty.isRemote() ? "-G" : "-J") + jmProperty.toString());
            }
        }
        log("Executing test plan: " + testPlanFile + " CommandLine is " + Arrays.toString(cmd.getCommandline()), Project.MSG_INFO);
        Execute execute = new Execute(new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN));
        execute.setCommandline(cmd.getCommandline());
        execute.setAntRun(getProject());

        execute.setWorkingDirectory(new File(jmeterHome.getAbsolutePath() + File.separator + "bin"));
        log(cmd.describeCommand(), Project.MSG_VERBOSE);

        try {
            execute.execute();
        }
        catch (IOException e) {
            throw new BuildException("JMeter execution failed.", e, getLocation());
        }
    }
}
